package tirateima.controlador;

import java.awt.Color;
import java.awt.EventQueue;
import java.util.concurrent.ArrayBlockingQueue;

import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.NavigationFilter;
import javax.swing.text.NavigationFilter.FilterBypass;
import javax.swing.text.Position.Bias;
import javax.swing.undo.UndoableEdit;

class Asker implements Runnable {
	
	
	ArrayBlockingQueue<String> resultQueue = new ArrayBlockingQueue<String>(1);
	int initialPos;
	int maxLen;
	JTextArea run;
	Controlador c;
	CommandReadInput command;
	Asker(CommandReadInput comm, Controlador c, int maxLen, JTextArea run) {
		this.maxLen = maxLen;
		this.run = run;
		this.c = c;
		this.command = comm;
		// initialPos will be set in run()
	}
	final DocumentListener listener = 
		new DocumentListener() {
		public void insertUpdate(final DocumentEvent e) {
			EventQueue.invokeLater(
					new Runnable() {
						public void run() {
							try {
								String inserted = e.getDocument().getText(e.getOffset(), e.getLength());
								int i = inserted.indexOf('\n');
								if (i >= 0) {
									int offset = e.getOffset() + i;
									if (offset + 1 == e.getDocument().getLength()) {
										returnResponse();
									} 
									else {
										// remove the '\n' and put it at the end
										e.getDocument().remove(offset, 1);
										e.getDocument().insertString(e.getDocument().getLength(), "\n", null);
										//insertUpdate will be called again, since we have inserted the '\n' at the end
									}
								} 
								else if (maxLen >= 0 && e.getDocument().getLength() - initialPos >= maxLen) {
									returnResponse();
								}
							} 
							catch (BadLocationException ex) {
								returnResponse();
							}
						}
					});
		}
		public void removeUpdate(final DocumentEvent e) {
			EventQueue.invokeLater(
					new Runnable() {
						public void run() {
							if ((e.getDocument().getLength() < initialPos || e.getOffset() < initialPos) && e instanceof UndoableEdit) {
								//((UndoableEdit) e).undo();
									run.setCaretPosition(e.getOffset() + e.getLength());
								
							}
						}
					});
		}
		public void changedUpdate(DocumentEvent e) { }
	};
	final NavigationFilter navigationFilter = 
		new NavigationFilter() {
		public void moveDot(FilterBypass fb, int dot, Bias bias) {
			if (dot < initialPos) {
				dot = Math.min(initialPos, run.getDocument().getLength());
			}
			fb.moveDot(dot, bias);
		}
		public void setDot(FilterBypass fb, int dot, Bias bias) {
			if (dot < initialPos) {
				dot = Math.min(initialPos, run.getDocument().getLength());
			}
			fb.setDot(dot, bias);
		}
	};
	/*final Simulator.StopListener stopListener = 
		new Simulator.StopListener() {
		public void stopped(Simulator s) {
			returnResponse();
		}
	};*/
	public void run() { // must be invoked from the GUI thread
		//setSelectedComponent(runTab);
		run.requestFocus();
		run.setEditable(true);
		run.setCaretColor(Color.white);
		run.requestFocusInWindow();
		run.setCaretPosition(run.getDocument().getLength());
		initialPos = run.getCaretPosition();
		run.setNavigationFilter(navigationFilter);
		run.getDocument().addDocumentListener(listener);
		//Simulator.getInstance().addStopListener(stopListener);
	}
	void cleanup() { // not required to be called from the GUI thread
		EventQueue.invokeLater(
				new Runnable() {
					public void run() {
						run.getDocument().removeDocumentListener(listener);
						run.setEditable(false);
						run.setCaretColor(Color.black);
						run.setNavigationFilter(null);
						run.setCaretPosition(run.getDocument().getLength());
						//c.console.setEstado(run.getText());
						SwingUtilities.invokeLater(new Runnable() {
							public void run() {
								if (command.finalizou_leitura) {
									c.proxEstadoEnabled(true);
								}
							}
						});
						//Simulator.getInstance().removeStopListener(stopListener);
					}
				});
	}
	void returnResponse() {
		try {
			int p = Math.min(initialPos, run.getDocument().getLength());
			int l = Math.min(run.getDocument().getLength() - p, maxLen >= 0 ? maxLen : Integer.MAX_VALUE);
			resultQueue.offer(run.getText(p, l));
		} 
		catch (BadLocationException ex) {
			// this cannot happen
			resultQueue.offer("");
		}
	}
	String response() {
		EventQueue.invokeLater(this);
		try {
			String str = resultQueue.take();
			str = str.replaceAll("\n", "");
			return str;
		} 
		catch (InterruptedException ex) {
			return null;
		} 
		finally {
			cleanup();
		}
	}
	
}